//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//[Natural Graphics for San Andreas by ZIMMER]
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//Depth Of Field by kingeric1992
//DOF weight - Marty McFly

#define MF_MODE	1
float FocalPlaneDepth=0;
float FarBlurDepth=100;	
float BokehBias=1;	
float BokehBiasCurve=2;	
float ZIMMER=3.0;

float NoiseAmount=0.00;
float offset_bias = 0.25;
float sharp_strength_luma = 0.2;
float sharp_clamp = 0.5;

//+++++++++++++++++++++++++++++
//external parameters, do not modify
//+++++++++++++++++++++++++++++
//keyboard controlled temporary variables (in some versions exists in the config file). Press and hold key 1,2,3...8 together with PageUp or PageDown to modify. By default all set to 1.0
float4 	tempF1; 	//0,1,2,3
float4 	tempF2; 	//5,6,7,8
float4 	tempF3; 	//9,0
//x=Width, y=1/Width, z=ScreenScaleY, w=1/ScreenScaleY
float4 	ScreenSize;	
//x=generic timer in range 0..1, period of 16777216 ms (4.6 hours), w=frame time elapsed (in seconds)
float4 	Timer;	
//adaptation delta time for focusing
float FadeFactor;
//transposed transform matrix view*projection and inverse of it
float4	MatrixVP[4];
float4	MatrixInverseVP[4];

//textures
texture2D texColor;
texture2D texDepth;
texture2D texNoise;
texture2D texFocus;	

sampler2D SamplerColor = sampler_state
{
	Texture = <texColor>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;//NONE;
	AddressU = Clamp;
	AddressV = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerDepth = sampler_state
{
	Texture = <texDepth>;
	MinFilter = POINT;
	MagFilter = POINT;
	MipFilter = NONE;
	AddressU = Clamp;
	AddressV = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerNoise = sampler_state
{
	Texture = <texNoise>;
	MinFilter = POINT;
	MagFilter = POINT;
	MipFilter = NONE;//NONE;
	AddressU = Wrap;
	AddressV = Wrap;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

sampler2D SamplerFocus = sampler_state
{
	Texture = <texFocus>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = NONE;
	AddressU = Clamp;
	AddressV = Clamp;
	SRGBTexture=FALSE;
	MaxMipLevel=0;
	MipMapLodBias=0;
};

struct VS_OUTPUT_POST
{
	float4 vpos : POSITION;
	float2 txcoord : TEXCOORD0;
};

struct VS_INPUT_POST
{
	float3 pos : POSITION;
	float2 txcoord : TEXCOORD0;
};

float linearizeDepth(float nonlinearDepth)
{
	float2 dofProj=float2(0.15, 3000);
	float2 dofDist=float2(0.0, 0.15);
	float4 depth=nonlinearDepth;
	
	depth.y=-dofProj.x + dofProj.y;
	depth.y=1.0/depth.y;
	depth.z=depth.y * dofProj.y;
	depth.z=depth.z * -dofProj.x;
	depth.x=dofProj.y * -depth.y + depth.x;
	depth.x=1.0/depth.x;
	depth.y=depth.z * depth.x;
	depth.x=depth.z * depth.x - dofDist.y;
	depth.x+=dofDist.x * -0.5;
	depth.x=max(depth.x, 0.0);
	return depth.x;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

VS_OUTPUT_POST VS_Focus(VS_INPUT_POST IN)
{
	VS_OUTPUT_POST OUT;
	float4 pos=float4(IN.pos.x,IN.pos.y,IN.pos.z,1.0);

	OUT.vpos=pos;
	OUT.txcoord.xy=IN.txcoord.xy;
	return OUT;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

VS_OUTPUT_POST VS_PostProcess(VS_INPUT_POST IN)
{
	VS_OUTPUT_POST OUT;
	float4 pos=float4(IN.pos.x,IN.pos.y,IN.pos.z,1.0);

	OUT.vpos=pos;
	OUT.txcoord.xy=IN.txcoord.xy;

	return OUT;
}

float4 PS_PostProcess(VS_OUTPUT_POST i) : COLOR
{
	#define FXAA_SUBPIX_SHIFT (1.0/8.0)
	#define FXAA_REDUCE_MIN   (1.0/32.0)
	#define FXAA_REDUCE_MUL   (1.0/16.0)
	#define FXAA_SPAN_MAX     8.0

	half2 rcpFrame = half2(1/ScreenSize.x, 1/(ScreenSize.x/ScreenSize.z));

	half4 posPos;
	posPos.xy = i.txcoord.xy;
	posPos.zw = posPos.xy - (rcpFrame.xy * (0.5 + FXAA_SUBPIX_SHIFT));

	half3 rgbNW = tex2D(SamplerColor, posPos.zw ).xyz;
	half3 rgbNE = tex2D(SamplerColor, posPos.zw + half2(rcpFrame.x, 0.0) ).xyz;
	half3 rgbSW = tex2D(SamplerColor, posPos.zw + half2(0.0, rcpFrame.y) ).xyz;
	half3 rgbSE = tex2D(SamplerColor, posPos.zw +rcpFrame.xy ).xyz;
	half3 rgbM  = tex2D(SamplerColor, posPos.xy).xyz;
       
	half3 luma = half3(0.299, 0.587, 0.114);
	half lumaNW = dot(rgbNW, luma);
	half lumaNE = dot(rgbNE, luma);
	half lumaSW = dot(rgbSW, luma);
	half lumaSE = dot(rgbSE, luma);
	half lumaM  = dot(rgbM,  luma);
       
	half lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
	half lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
       
	half2 dir; 
       
	half lumaNWNE = lumaNW + lumaNE;
	half lumaSWSE = lumaSW + lumaSE;
       
	dir.x = -((lumaNWNE) - (lumaSWSE));
	dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));
       
	half dirReduce = max( (lumaSWSE + lumaNWNE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
	half rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
	dir = min(half2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX), max(half2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * rcpFrame.xy;
             
	half3 rgbA = (1.0/2.0) * (tex2D(SamplerColor, posPos.xy + dir * (1.0/3.0 - 0.5) ).xyz + tex2D(SamplerColor, posPos.xy + dir * (2.0/3.0 - 0.5) ).xyz);
	half3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (tex2D(SamplerColor, posPos.xy + dir * (0.0/3.0 - 0.5) ).xyz + tex2D(SamplerColor, posPos.xy + dir * (3.0/3.0 - 0.5) ).xyz);
	half lumaB = dot(rgbB, luma);
     
	if((lumaB < lumaMin) || (lumaB > lumaMax))
 		return half4(rgbA, 1.0);
       
	return float4(rgbB, 1.0);
}

float4 PS_PostProcess_Sharp_Noise(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
{
	float4 res = 0;
	float4 tex=0.0;
	tex.xy=IN.txcoord.xy;

	float3 ori = tex2D(SamplerColor, tex).rgb;       // ori = original pixel

	float2 p;
	p.x = 1.0/ScreenSize.x;
	p.y=p.x/ScreenSize.z;

	// -- Gaussian filter --
	//   [ .50, .50, .50]     [ 1 , 1 , 1 ]
	//   [ .50,    , .50]  =  [ 1 ,   , 1 ]
 	//   [ .50, .50, .50]     [ 1 , 1 , 1 ]

    float3 blur_ori = tex2D(SamplerColor, tex + float2(0.5 * p.x,-p.y * offset_bias)).rgb;  // South South East
    blur_ori += tex2D(SamplerColor, tex + float2(offset_bias * -p.x,0.5 * -p.y)).rgb; // West South West
    blur_ori += tex2D(SamplerColor, tex + float2(offset_bias * p.x,0.5 * p.y)).rgb; // East North East
    blur_ori += tex2D(SamplerColor, tex + float2(0.5 * -p.x,p.y * offset_bias)).rgb; // North North West

    //blur_ori += (2 * ori); // Probably not needed. Only serves to lessen the effect.
	
    blur_ori /= 4.0;  //Divide by the number of texture fetches

    //sharp_strength_luma *= 0.666; // Adjust strength to aproximate the strength of pattern 2
	
	// -- Calculate the sharpening --  
	float3 sharp = ori - blur_ori;  //Subtracting the blurred image from the original image
  
	// -- Adjust strength of the sharpening --
	float sharp_luma = dot(sharp, sharp_strength_luma); //Calculate the luma and adjust the strength

	// -- Clamping the maximum amount of sharpening to prevent halo artifacts --
	sharp_luma = clamp(sharp_luma, -sharp_clamp, sharp_clamp);  //TODO Try a curve function instead of a clamp

	// -- Combining the values to get the final sharpened pixel	--
  //float4 done = ori + sharp_luma;    // Add the sharpening to the original.
	float4 done = tex2D(SamplerColor, tex) + sharp_luma;    // Add the sharpening to the input color.

 

	// -- Grain noise
	float origgray=dot(done.xyz, 0.333);
	//origgray=max(origgray, res.z);
	tex.xy=IN.txcoord.xy*16.0 + origgray;
	float4 cnoi=tex2Dlod(SamplerNoise, tex);
	done=lerp(done, (cnoi.x+0.5)*done, NoiseAmount*saturate(1.0-origgray*1.8));


	done.w=1.0;
	return done;
}

//Calculate CoC & Tilt_Shift
float4 PS_CoCtoAlpha(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
{	
	float2 coord = IN.txcoord.xy;
	float4 res = tex2D(SamplerColor, coord.xy);;
	float scenefocus = tex2D(SamplerFocus, 0.5).x;
	float depth = linearizeDepth(tex2D(SamplerDepth, IN.txcoord.xy).x);
	
	int MF = (MF_MODE == 1)? 1 : 0;
	float focalPlaneDepth = lerp(scenefocus, FocalPlaneDepth, MF);
	float farBlurDepth = lerp(scenefocus*pow(4.0,0), FarBlurDepth, MF);		
		
	if(depth < focalPlaneDepth)
		res.w=(depth - focalPlaneDepth)/focalPlaneDepth;
	else
	{
		res.w=(depth - focalPlaneDepth)/(farBlurDepth - focalPlaneDepth);
		res.w=saturate(res.w);
	}
	
	res.w=res.w * 0.5 + 0.5;
	return res;
}

//Dof pass
float4 PS_BokehCircle(VS_OUTPUT_POST IN, float2 vPos : VPOS) : COLOR
{
	float2 coord=IN.txcoord.xy;
	float4 origcolor=tex2D(SamplerColor, coord.xy);
	float centerDepth=origcolor.w;
	float2 pixelSize=ScreenSize.y;
	pixelSize.y*=ScreenSize.z;

	float blurAmount=abs(centerDepth * 2.0 - 1.0);
	float discRadius=blurAmount * ZIMMER;

	float4 res;
	float3 weight = 1;
	res.xyz = origcolor.xyz;
	res.xyz *= lerp(1.0, 0, BokehBias);	
	res.xyz = pow(res.xyz, ZIMMER) * weight;
	res.w = weight.x;

	float sampleCycleCounter;
	float sampleCounterInCycle;
	float2 sampleOffset;
	int dofTaps = 1 * (1 + 1) * 4;

	for(int i=0; i < 250 && i < dofTaps; i++)
	{
		if((sampleCounterInCycle % (sampleCycleCounter * 8)) == 0)
		{
			sampleCounterInCycle = 0;
			sampleCycleCounter++;
		}
		
		float sampleAngle = 0.78539816 * ( sampleCounterInCycle / sampleCycleCounter);
		sampleCounterInCycle++;
		sincos(sampleAngle, sampleOffset.y, sampleOffset.x);
		sampleOffset *= pixelSize * discRadius * sampleCycleCounter / 1;
	
		float4 tap = tex2Dlod(SamplerColor, float4(coord + sampleOffset, 0, 0));
		weight.xyz = (tap.a >= centerDepth)? 1.0 : abs(tap.a * 2.0 - 1.0);	
		tap.xyz *= lerp(1.0, pow(sampleCycleCounter/ 1, BokehBiasCurve), BokehBias);

		res.xyz += pow(tap.xyz * weight.xyz, ZIMMER);
		res.w += pow(weight.y, ZIMMER);
	}
	res.xyz = pow( res.xyz / res.w, 1 / ZIMMER);
	res.w = centerDepth;

	return res;
}

float4 PS_Guassian(VS_OUTPUT_POST IN, float2 vPos : VPOS, uniform float BlurStrength) : COLOR
{
	float2 coord = IN.txcoord.xy;
	float2 pixelSize = ScreenSize.y;
	pixelSize.y *= ScreenSize.z;
	float4 origcolor = tex2D(SamplerColor, coord.xy);
	
	float blurAmount = abs(origcolor.w * 2.0 - 1.0) * ZIMMER;
	blurAmount /= 10 * (1 + ((3.0 - 0) % 3));

	float weight[5] = {0.227027,0.194595,0.121622,0.0540541,0.0162162};
	float4 res=origcolor * weight[0];

	for(int i=1; i < 5; i++)
	{
		res	+= tex2D(SamplerColor, coord + float2(i * pixelSize.x * blurAmount * BlurStrength, 0)) * weight[i];
		res	+= tex2D(SamplerColor, coord - float2(i * pixelSize.x * blurAmount * BlurStrength, 0)) * weight[i];
	}
	return res;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
technique PostProcess
{
	pass P0
	{

		VertexShader = compile vs_3_0 VS_PostProcess();
		PixelShader = compile ps_3_0 PS_CoCtoAlpha();

		DitherEnable=FALSE;
		ZEnable=FALSE;
		CullMode=NONE;
		ALPHATESTENABLE=FALSE;
		SEPARATEALPHABLENDENABLE=FALSE;
		AlphaBlendEnable=FALSE;
		StencilEnable=FALSE;
		FogEnable=FALSE;
		SRGBWRITEENABLE=FALSE;
	}
}

technique PostProcess2
{
	pass P0
	{
		VertexShader = compile vs_3_0 VS_PostProcess();
		PixelShader = compile ps_3_0 PS_BokehCircle();

		DitherEnable=FALSE;
		ZEnable=FALSE;
		CullMode=NONE;
		ALPHATESTENABLE=FALSE;
		SEPARATEALPHABLENDENABLE=FALSE;
		AlphaBlendEnable=FALSE;
		StencilEnable=FALSE;
		FogEnable=FALSE;
		SRGBWRITEENABLE=FALSE;
	}
}

technique PostProcess3
{
	pass P0
	{
		VertexShader = compile vs_3_0 VS_PostProcess();
		PixelShader = compile ps_3_0 PS_Guassian(0);

		DitherEnable=FALSE;
		ZEnable=FALSE;
		CullMode=NONE;
		ALPHATESTENABLE=FALSE;
		SEPARATEALPHABLENDENABLE=FALSE;
		AlphaBlendEnable=FALSE;
		StencilEnable=FALSE;
		FogEnable=FALSE;
		SRGBWRITEENABLE=FALSE;
	}
}

technique PostProcess4 //FXAA
{
   pass P0
   {
	VertexShader = compile vs_3_0 VS_PostProcess();
	PixelShader  = compile ps_3_0 PS_PostProcess_Sharp_Noise();

	FogEnable=FALSE;
	ALPHATESTENABLE=FALSE;
	SEPARATEALPHABLENDENABLE=FALSE;
	AlphaBlendEnable=FALSE;
	FogEnable=FALSE;
	SRGBWRITEENABLE=FALSE;
   }
}

technique PostProcess5 //FXAA
{
   pass P0
   {
	VertexShader = compile vs_3_0 VS_PostProcess();
	PixelShader  = compile ps_3_0 PS_PostProcess();

	FogEnable=FALSE;
	ALPHATESTENABLE=FALSE;
	SEPARATEALPHABLENDENABLE=FALSE;
	AlphaBlendEnable=FALSE;
	FogEnable=FALSE;
	SRGBWRITEENABLE=FALSE;
   }
}

technique PostProcess6 //FXAA
{
   pass P0
   {
	VertexShader = compile vs_3_0 VS_PostProcess();
	PixelShader  = compile ps_3_0 PS_PostProcess_Sharp_Noise();

	FogEnable=FALSE;
	ALPHATESTENABLE=FALSE;
	SEPARATEALPHABLENDENABLE=FALSE;
	AlphaBlendEnable=FALSE;
	FogEnable=FALSE;
	SRGBWRITEENABLE=FALSE;
   }
}

